class TreeNode {
  TreeNode(this._checked);

  bool _checked;

  int _level = 0;

  int get level => _level;

  set level(int level) {
    _level = level;
    if (_children.isNotEmpty) {
      for (var value in _children) {
        value.level = level + 1;
      }
    }
  }

  bool get checked => _checked;

  TreeNode parent;

  set checkState(SelectedState state) {
    assert(state != null);
    assert(state != SelectedState.half);

    if (_children.isNotEmpty) {
      for (var value in _children) {
        value.checkState = state;
      }
    } else {
      this._checked = state == SelectedState.selected;
    }
  }

  SelectedState get checkState {
    if (_children.isEmpty) {
      return checked == true ? SelectedState.selected : SelectedState.no;
    } else {
      int pickCount = 0;
      int noCount = 0;
      int totalCount = _children.length;
      for (final child in _children) {
        if (child.checkState == SelectedState.no) {
          noCount++;
        } else if (child.checkState == SelectedState.selected) {
          pickCount++;
        }
      }
      if (noCount == totalCount) {
        return SelectedState.no;
      } else if (pickCount == totalCount) {
        return SelectedState.selected;
      } else {
        return SelectedState.half;
      }
    }
  }

  List<TreeNode> _children = [];

  List<TreeNode> get children => _children.toList();

  void addChild(TreeNode node) {
    _children.add(node);
    node.level = level + 1;
    node.parent = this;
  }

  void removeChild(TreeNode node) {
    _children.remove(node);
    node.parent = null;
  }
}

class NamedNode extends TreeNode {
  String name;

  NamedNode({
    this.name,
    bool checked = false,
  }) : super(checked);
}

enum SelectedState {
  selected,
  half,
  no,
}
